// modified from branch:all_flow_level opearatorUniAttr.h
#pragma once

#include <vector>
#include <unordered_map>
#include <functional>
#include <sstream>
#include "TcpLayer.h"
#include <UdpLayer.h>
#include "IPv4Layer.h"
#include "PayloadLayer.h"
#include "PacketUtils.h"
#include "SystemUtils.h"
#include <ctime>
#include<fstream> 
#include <headers/unifiedPacketAttr.h>

// 每条会话最多包数
#define MAX_SIZE_PAC_LEN 100

struct flowUniAttr
{
    // vector type is sequence by default. 把所有的uint8都转成uint16，防止出现类型问题，比如写入查看csv为NULL
    uint32_t hash_val;
    std::string src_ip;
    std::string dst_ip;
    uint16_t protocol;                   // 传输层协议类型tcp,udp,icmp,0,1,2
    uint16_t  src_port;                 // 源端口,icmp没有
    uint16_t  dst_port;                 // 目的端口,icmp没有
    size_t packet_count =0;                // 一条流的包个数
    //通用属性
    std::vector<uint32_t> packet_len;//包长序列
    std::vector<uint32_t> tv_sec;
    std::vector<uint32_t> tv_nsec;
    // ipv4 属性
    std::vector<uint16_t> ttl;
    std::vector<uint16_t> tos;
    std::vector<uint16_t> id;
    std::vector<uint16_t> offset;
    std::vector<uint16_t> protocol_ip;
    // 传输层通用属性
    std::vector<uint16_t> len_load;      // 负载长度
    std::vector<uint32_t> ack_num;       // 确认号，udp没有
    std::vector<uint32_t> seq_num;      // 序列号，udp没有
    // tcp属性
    std::vector<uint16_t> flag;        // 标志位
    std::vector<uint16_t> window; // 窗口大小
    // udp属性
    std::vector<uint16_t> len_udp;// udp长度
    // icmp属性
    std::vector<uint16_t> icmp_type; // icmp类型
    std::vector<uint16_t> icmp_code; // icmp代码

    void clear()
		{   ;
		}

    void resize()
    {//截断过长的数组，同时填0:由于大于的直接就没处理,此处不做处理;TODO:fill 0
        ;
    }
};

// 序列到csv的工具函数,泛型
template<typename T>
void sequence_to_csv(const std::vector<T>& seq, std::ofstream& outputF) {
    for (const auto& val : seq) {
        outputF << val << ',';
    }
}

class operator_UniAttr
{   
    private:
        //给定hash_val，查找流统一属性
        std::unordered_map<uint32_t, flowUniAttr> m_FlowTable;
    public:
        long long packet_count = 0;  //operator处理的所有流的总包数
        void handle_packet(pcpp::Packet* packet)
        {
            // 根据一个包的五元组计算 会话ID
            packet_count++;
            unifiedPacketAttr packet_attr(packet);  //解析得到想要的一个包的统一属性

            uint32_t hashVal = packet_attr.hash_val;
            if (m_FlowTable.find(hashVal) == m_FlowTable.end())   // 如果是新会话,该包是流的第一个包,首先清空表，然后设定hashVal+五元组：
            {   
                m_FlowTable[hashVal].clear();

                m_FlowTable[hashVal].hash_val = hashVal;
                m_FlowTable[hashVal].src_ip = packet_attr.src_ip ;
                m_FlowTable[hashVal].dst_ip = packet_attr.dst_ip;
                m_FlowTable[hashVal].src_port = packet_attr.src_port;
                m_FlowTable[hashVal].dst_port = packet_attr.dst_port;
                m_FlowTable[hashVal].protocol = packet_attr.protocol;
            }
            if (m_FlowTable[hashVal].packet_count>=MAX_SIZE_PAC_LEN) //一条流超过了需要的个数，就不处理了
            {
                return;
            }
            
            m_FlowTable[hashVal].packet_count++;

            m_FlowTable[hashVal].packet_len          .push_back(packet_attr.packet_len);
            m_FlowTable[hashVal].tv_sec               .push_back(packet_attr.tv_sec);
            m_FlowTable[hashVal].tv_nsec          .push_back(packet_attr.tv_nsec);
            m_FlowTable[hashVal].ttl          .push_back(packet_attr.ttl);
            m_FlowTable[hashVal].tos          .push_back(packet_attr.tos);
            m_FlowTable[hashVal].id          .push_back(packet_attr.id);
            m_FlowTable[hashVal].offset          .push_back(packet_attr.offset);
            m_FlowTable[hashVal].protocol_ip          .push_back(packet_attr.protocol_ip);
            m_FlowTable[hashVal].len_load          .push_back(packet_attr.len_load);
            m_FlowTable[hashVal].ack_num          .push_back(packet_attr.ack_num);
            m_FlowTable[hashVal].seq_num          .push_back(packet_attr.seq_num);
            m_FlowTable[hashVal].flag          .push_back(packet_attr.flag);
            m_FlowTable[hashVal].window          .push_back(packet_attr.window);
            m_FlowTable[hashVal].len_udp          .push_back(packet_attr.len_udp);
            m_FlowTable[hashVal].icmp_type          .push_back(packet_attr.icmp_type);
            m_FlowTable[hashVal].icmp_code          .push_back(packet_attr.icmp_code);
        }

        void to_csv(std::ofstream& outputFile, const std::string& tag)
        {   //写入文件
            std::vector<flowUniAttr> attr_l;

            for (auto& pair : m_FlowTable) {
                if (pair.second.packet_count>=MAX_SIZE_PAC_LEN) //超短流不做处理
                {
                    pair.second.resize();
                    attr_l.push_back(pair.second);
                }
            }
            
          
            // 遍历unordered_map并写入每个值
            for (const auto& item : attr_l) {
                // 写入hash和五元组
                outputFile << item.hash_val << ',';
                outputFile << item.src_ip << ',';
                outputFile << item.dst_ip << ',';
                outputFile << item.src_port << ',';
                outputFile << item.dst_port << ',';
                outputFile << item.protocol << ',';
                
                sequence_to_csv(item.packet_len ,outputFile);
                sequence_to_csv(item.tv_sec     ,outputFile);
                sequence_to_csv(item.tv_nsec    ,outputFile);
                sequence_to_csv(item.ttl        ,outputFile);
                sequence_to_csv(item.tos        ,outputFile);
                sequence_to_csv(item.id         ,outputFile);
                sequence_to_csv(item.offset     ,outputFile);
                sequence_to_csv(item.protocol_ip,outputFile);
                sequence_to_csv(item.len_load   ,outputFile);
                sequence_to_csv(item.ack_num    ,outputFile);
                sequence_to_csv(item.seq_num    ,outputFile);
                sequence_to_csv(item.flag       ,outputFile);
                sequence_to_csv(item.window     ,outputFile);
                sequence_to_csv(item.len_udp    ,outputFile);
                sequence_to_csv(item.icmp_type  ,outputFile);
                sequence_to_csv(item.icmp_code  ,outputFile);
                outputFile << tag << '\n'; //every row ends with no common.
            }
        }
};